﻿using System.Collections.Generic;
using UnityEngine;

namespace Game
{
    public abstract class GameControllerBase : MonoBehaviour
    {
        /// <summary>
        /// 游戏实例
        /// </summary>
        static GameControllerBase g_instance;

        //--Game status
        protected enum GameStatus { Play = 0, Pause, GameOver_Lose, GameOver_Win };
        protected GameStatus m_gameStatus = GameStatus.Play;

        /// <summary>
        /// 监听事件：游戏重新开始
        /// </summary>
        Dictionary<string, CallbackFunc> m_onReplay = new Dictionary<string, CallbackFunc>();

        /// <summary>
        /// 监听事件：游戏结束了，并且游戏失败
        /// </summary>
        Dictionary<string, CallbackFuncWithBool> m_onGameOver = new Dictionary<string, CallbackFuncWithBool>();

        protected virtual void Awake()
        {
            g_instance = this;
            Debug.Log("awake instance;");

            //监听的字典初始化增加default的：
            m_onGameOver[C_DefaultCallerId] = null;
            m_onReplay[C_DefaultCallerId] = null;
        }

        protected virtual void Update()
        {

            //Restart the level
            if (Input.GetKeyUp("f2"))
            {
                DoReplayGame();
                return;
            }

            if (m_gameStatus == GameStatus.GameOver_Lose)
            {
                if (Input.GetKeyUp(KeyCode.Return))
                {
                    DoReplayGame();
                }
            }


        }

        //---------------------------------------------------------------------------
        //				Public area
        //---------------------------------------------------------------------------
        #region Public area
        const string C_DefaultCallerId = "Default";
        /// <summary>
        /// 事件监听:游戏重新开始
        /// <para>注意：当场景被重新读取时，instance也将重新加载。对应的，全局的回调函数也将消失。</para>
        /// </summary>
        /// <param name="onGameReplay">游戏重新开始</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        public static void AddOnGameReplayOnce(CallbackFunc onGameReplay, string callerId)
        {
            AddOnGameReplay(onGameReplay, callerId);
        }

        /// <summary>
        /// 事件监听:游戏重新开始
        /// <para>注意：当场景被重新读取时，instance也将重新加载。对应的，全局的回调函数也将消失。</para>
        /// </summary>
        /// <param name="onGameReplay">游戏重新开始</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        public static void AddOnGameReplay(CallbackFunc onGameReplay)
        {
            AddOnGameReplay(onGameReplay, C_DefaultCallerId);
        }

        /// <summary>
        /// 事件监听：游戏结束
        /// </summary>
        /// <param name="onGameOverAndLose">param: is GameOver?</param>
        public static void AddOnGameOver(CallbackFuncWithBool onGameOverAndLose)
        {
            AddOnGameOver(onGameOverAndLose, C_DefaultCallerId);
        }

        /// <summary>
        /// 事件监听：游戏结束
        /// </summary>
        /// <param name="onGameOverAndLose">param: is GameOver?</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        public static void AddOnGameOverOnce(CallbackFuncWithBool onGameOverAndLose, string callerId)
        {
            AddOnGameOver(onGameOverAndLose, callerId);
        }

        /// <summary>
        /// 事件监听：游戏结束
        /// </summary>
        /// <param name="onGameOverAndLose">param: is GameOver?</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        public static void AddOnGameOverOnce(CallbackFuncWithBool onGameOverAndLose, System.Type callerId)
        {
            AddOnGameOver(onGameOverAndLose, callerId.Name);
        }

#if UNITY_EDITOR
        static bool b_logNoInstance = false;
#endif
        /// <summary>
        /// 游戏是否正运行中
        /// </summary>
        /// <returns></returns>
        public static bool IsPlaying()
        {
            #if UNITY_EDITOR
            if(!g_instance )
            {
                if (!b_logNoInstance)
                {
                    b_logNoInstance = true;
                    Debug.Log("No game controller.");
                }
                return true;
            }
            #endif

            return g_instance.m_gameStatus == GameStatus.Play;
        }

        /// <summary>
        /// 是否游戏结束且输了
        /// </summary>
        /// <returns><c>true</c>, if game over was ised, <c>false</c> otherwise.</returns>
        public static bool IsLose()
        {
            return g_instance.m_gameStatus == GameStatus.GameOver_Lose;
        }

        /// <summary>
        /// 游戏胜利？
        /// </summary>
        /// <returns><c>true</c>, if window was ised, <c>false</c> otherwise.</returns>
        public static bool IsWin()
        {
            return g_instance.m_gameStatus == GameStatus.GameOver_Win;
        }

        public static void DoGameWin()
        {
            if (IsLose())
            {
                Debug.Log("Cannot enter win status.");
                return;
            }

            g_instance.m_gameStatus = GameStatus.GameOver_Win;

            if (!GameObjFunc.IsNullOrEmpty(g_instance.m_onGameOver))
            {
                DoCallbakc(g_instance.m_onGameOver, false);
                //instance.m_onGameOver(false);                     //成功了
            }
        }

        /// <summary>
        /// 设置游戏结束
        /// </summary>
        public static void DoGameOver()
        {
            if (IsWin())
            {
                Debug.Log("Cannot enter game over status");
                return;
            }

            //状态切换
            g_instance.m_gameStatus = GameStatus.GameOver_Lose;

            if (!GameObjFunc.IsNullOrEmpty(g_instance.m_onGameOver))
            {
                DoCallbakc(g_instance.m_onGameOver, true);
                //instance.m_onGameOver(true);                     //失败了
            }
        }

        /// <summary>
        /// 设置游戏开始
        /// </summary>
        /// <param name="forceReloadScene">强制重新读取关卡数据，而不是在关卡内重设游戏状态</param>
        public static void DoReplayGame(bool forceReloadScene = false)
        {
            if (forceReloadScene)
            {
                SceneControllerBase.ReloadGame();
            }
            else
            {
                //--不重新读取游戏，而是各归初始之位：
                ////游戏状态重置：
                g_instance.m_gameStatus = GameStatus.Play;

                //操作重置
                MyInput.ResetAxis();

            }

            DoCallbakc(g_instance.m_onReplay);

        }



        public static void DoPlayNextScene()
        {
            Debug.Log("Play next");
        }


        /// <summary>
        /// 暂停或继续游戏
        /// </summary>
        public static void DoPauseOrPlay()
        {
            if (g_instance.m_gameStatus == GameStatus.Play)
            {
                g_instance.m_gameStatus = GameStatus.Pause;
            }
            else if (g_instance.m_gameStatus == GameStatus.Pause)
            {
                g_instance.m_gameStatus = GameStatus.Play;
            }
        }
        #endregion

        //---------------------------------------------------------------------------
        //				Private area
        //---------------------------------------------------------------------------
        #region Private area

        /// <summary>
        /// 事件监听:游戏重新开始
        /// <para>注意：当场景被重新读取时，instance也将重新加载。对应的，全局的回调函数也将消失。</para>
        /// </summary>
        /// <param name="onGameReplay">游戏重新开始</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        private static void AddOnGameReplay(CallbackFunc onGameReplay, string callerId)
        {
            if (g_instance == null)
            {
                Debug.LogWarning("No game controller found.");
                return;
            }

            if (onGameReplay != null)
            {
                if (callerId == C_DefaultCallerId)
                {
                    g_instance.m_onReplay[callerId] += onGameReplay;
                }
                else
                {
                    //不存在该id的回调时才注册：
                    if (!g_instance.m_onGameOver.ContainsKey(callerId))
                    {
                        g_instance.m_onReplay[callerId] = onGameReplay;
                    }
                }

            }
        }


        /// <summary>
        /// 事件监听：游戏结束
        /// </summary>
        /// <param name="onGameOverAndLose">param: is GameOver?</param>
        /// <param name="callerId">当指定callerID时，将在callerID的回调存在时，跳过该回调的注册</param>
        private static void AddOnGameOver(CallbackFuncWithBool onGameOverAndLose, string callerId)
        {
            if (onGameOverAndLose != null)
            {
                if (callerId == C_DefaultCallerId)
                {
                    g_instance.m_onGameOver[callerId] += onGameOverAndLose;
                }
                else
                {
                    //不存在该id的回调时才注册：
                    if (!g_instance.m_onGameOver.ContainsKey(callerId))
                    {
                        g_instance.m_onGameOver[callerId] = onGameOverAndLose;
                    }
                }

            }
        }

        private static void DoCallbakc(Dictionary<string, CallbackFunc> callbacksDict)
        {

            if (!GameObjFunc.IsNullOrEmpty(callbacksDict))
            {
                foreach (var callback in callbacksDict.Values)
                {
                    if (callback != null)
                    {
                        callback();
                        Debug.Log("Callback replay");
                    }
                }
            }
        }

        private static void DoCallbakc(Dictionary<string, CallbackFuncWithBool> callbacksDict, bool param)
        {
            if (!GameObjFunc.IsNullOrEmpty(callbacksDict))
            {
                foreach (var callback in callbacksDict.Values)
                {
                    if (callback != null)
                    {
                        Debug.Log("Callback " + param);
                        callback(param);
                    }
                }
            }
        }

        #endregion

    }
}